Imaginez la situation: vous avez écrit un merveilleux code Python qui produit un beau graphique en tant que sortie. Vous enregistrez ce graphique, naturellement, comme graph.png . Vous exécutez le code plusieurs fois, apportant à chaque fois des modifications mineures. Vous y retournez la semaine / mois / an prochain. Savez-vous comment vous avez créé ce graphique? Quelles données d'entrée? Quelle version de votre code? Si vous êtes comme moi, la réponse sera souvent, frustrante, «non». Bien sûr, vous perdez ensuite beaucoup de temps à essayer de déterminer comment vous l'avez créé, ou même abandonner et ne jamais l'utiliser dans ce journal qui vous gagnera un prix Nobel…
Cet exposé présentera la destination (de Recipe et Python ), un module Python qui vous sauvera de cette situation! (Bien qu'il ne puisse pas garantir que votre article résultant gagnera un prix Nobel!) Avec l'ajout d'une seule ligne de code en haut de vos fichiers Python, la destination enregistrera chaque exécution de votre code dans une base de données, en gardant une trace de Les fichiers d'entrée, les fichiers de sortie et la version de votre code, puis vous permettent d'interroger cette base de données pour savoir comment vous avez réellement créé graph.png .
La façon la plus simple d'installer est simplement de courir
pip install recipy
Alternativement, vous pouvez cloner ce référentiel et exécuter:
python setup.py install
Si vous souhaitez installer les dépendances manuellement (elles doivent être installées automatiquement si vous suivez les instructions ci-dessus), exécutez:
pip install -r requirements.txt
Vous pouvez passer à partir d'une version précédente en fonctionnant:
pip install -U recipy
Pour découvrir ce qui a changé depuis la dernière version, voir le Changelog
Remarque: les versions précédentes (inédites) de la destination requises MongoDB sont installées et configurées manuellement. Ce n'est plus nécessaire, car une base de données Python pure (TINYDB) est utilisée à la place. De plus, l'interface graphique est désormais intégrée entièrement dans la destination et ne nécessite pas d'installation séparément.
Ajoutez simplement la ligne suivante en haut de votre script Python:
import recipyNotez que cela doit être la ligne supérieure de votre script, avant d'importer autre chose.
Ensuite, exécutez votre script comme d'habitude, et toutes les données seront connectées à la base de données TinyDB (ne vous inquiétez pas, la base de données est automatiquement créée si nécessaire). Vous pouvez ensuite utiliser le script recipy pour interroger rapidement la base de données pour savoir quel exécution de votre code a produit quel fichier de sortie. Ainsi, par exemple, si vous exécutez du code comme ceci:
import recipy
import numpy
arr = numpy . arange ( 10 )
arr = arr + 500
numpy . save ( 'test.npy' , arr ) (Notez l'ajout de import recipy au début du script - mais il n'y a pas d'autres modifications d'un script standard)
Alternativement, exécutez un script non modifié avec python -m recipy SCRIPT [ARGS ...] pour activer la journalisation de la destination. Cela invoque le point d'entrée du module de la destination, qui s'occupe de la destination d'importation pour vous, avant d'exécuter votre script.
Il produira une sortie appelée test.npy . Pour découvrir les détails de l'exécution qui a créé ce fichier que vous pouvez rechercher en utilisant
recipy search test.npy
et il affichera des informations comme ce qui suit:
Created by robin on 2015-05-25 19:00:15.631000
Ran /Users/robin/code/recipy/example_script.py using /usr/local/opt/python/bin/python2.7
Git: commit 91a245e5ea82f33ae58380629b6586883cca3ac4, in repo /Users/robin/code/recipy, with origin [email protected]:recipy/recipy.git
Environment: Darwin-14.3.0-x86_64-i386-64bit, python 2.7.9 (default, Feb 10 2015, 03:28:08)
Inputs:
Outputs:
/Users/robin/code/recipy/test.npy
Une autre façon de voir cela consiste à utiliser l'interface graphique. Il suffit d'exécuter recipy gui et une fenêtre de navigateur s'ouvrira avec une interface que vous pouvez utiliser pour rechercher toutes vos «exécutions» de destination:
Si vous souhaitez enregistrer les entrées et les sorties de fichiers lus ou écrits avec Open INTOL, vous devez faire un peu plus de travail. Soit utiliser recipy.open (nécessite uniquement import recipy en haut de votre script), soit ajouter from recipy import open et utiliser simplement open . Cette solution de contournement est requise, car de nombreuses bibliothèques utilisent l'ouverture intégrée en interne, et vous souhaitez uniquement enregistrer les fichiers que vous avez explicitement ouverts vous-même.
Si vous utilisez Python 2, vous pouvez passer un paramètre encoding à recipy.open . Dans ce cas, codecs sont utilisés pour ouvrir le fichier avec un codage approprié.
Une fois que vous avez des exécutions dans votre base de données, vous pouvez «annoter» ces exécutions avec toutes les notes que vous souhaitez les garder. Cela peut être particulièrement utile pour l'enregistrement qui fonctionne bien, ou des problèmes particuliers que vous rencontrez. Cela peut être fait à partir de la page «Détails» dans l'interface graphique, ou en exécutant
recipy annotate
qui ouvrira un éditeur pour vous permettre d'écrire des notes qui seront attachées à l'exécution. Ceux-ci seront ensuite visibles via la ligne de commande et l'interface graphique lors de la recherche d'exécutions.
Il existe également d'autres fonctionnalités dans l'interface de ligne de commande: recipy --help pour voir les autres options. Vous pouvez afficher les diffs, voir tous les exécutions qui ont créé un fichier avec un nom donné, une recherche basée sur les ID, afficher la dernière entrée et plus:
recipy - a frictionless provenance tool for Python
Usage:
recipy search [options] <outputfile>
recipy latest [options]
recipy gui [options]
recipy annotate [<idvalue>]
recipy (-h | --help)
recipy --version
Options:
-h --help Show this screen
--version Show version
-a --all Show all results (otherwise just latest result given)
-f --fuzzy Use fuzzy searching on filename
-r --regex Use regex searching on filename
-i --id Search based on (a fragment of) the run ID
-v --verbose Be verbose
-d --diff Show diff
-j --json Show output as JSON
--no-browser Do not open browser window
--debug Turn on debugging mode
La destination stocke toute sa configuration et la base de données elle-même dans ~/.recipy . Le fichier de configuration principal de la destination se trouve dans ce dossier, appelé recipyrc . Le format de fichier de configuration est très simple et est basé sur les fichiers Windows INI - et avoir un fichier de configuration est complètement facultatif: les valeurs par défaut fonctionneront bien sans fichier de configuration.
Un exemple de configuration est:
[ignored metadata]
diff
[general]
debug
Cela demande simplement à la récipient de ne pas enregistrer les informations git diff lorsqu'il enregistre les métadonnées sur une exécution, et également d'imprimer des messages de débogage (ce qui peut être vraiment utile si vous essayez de déterminer pourquoi certaines fonctions ne sont pas corrigées). Pour le moment, les seules options possibles sont:
[general]debug - Imprimer les messages de débogageeditor = vi - Configurez l'éditeur de texte par défaut qui sera utilisé lorsque la destination aura besoin de vous pour saisir un message. Utilisez le bloc-notes si sous Windows, par exemplequiet - n'imprimez aucun messageport - Spécifiez le port à utiliser pour l'interface graphique[data]file_diff_outputs - Stocker Diff entre l'ancienne sortie et le nouveau fichier de sortie, si le fichier de sortie existe avant l'exécution du script[database]path = /path/to/file.json - Définissez le chemin d'accès au fichier de base de données[ignored metadata]diff - Ne stockez pas la sortie de git diff dans les métadonnées pour une course de destinationgit - Ne stockez rien concernant Git (origine, engagement, repos, etc.) dans les métadonnées pour une course de destinationinput_hashes - Ne calculez pas et ne stockez pas les hachages SHA-1 de fichiers d'entréeoutput_hashes - Ne calculez pas et ne stockez pas les hachages SHA-1 des fichiers de sortie[ignored inputs]numpy ) pour demander à la récipient de ne pas enregistrer les entrées de ce module, ou all pour ignorer les entrées de tous les modules[ignored outputs]numpy ) pour demander à la récipient de ne pas enregistrer les sorties de ce module, ou all pour ignorer les sorties de tous les modules Par défaut, toutes les métadonnées sont stockées (c'est-à-dire aucune métadonnée n'est ignorée) et les messages de débogage ne sont pas affichés. Un fichier .recipyrc dans le répertoire actuel a priorité sur le fichier ~/.recipy/recipyrc , ce qui permet de gérer facilement les configurations par projet.
Remarque: Aucun fichier de configuration par défaut n'est fourni avec la destination, donc si vous souhaitez configurer tout ce qui vous aura besoin pour créer vous-même un fichier correctement formaté.
Lorsque vous importez un destinataire, il ajoute un certain nombre de classes à sys.meta_path . Ceux-ci sont ensuite utilisés par Python dans le cadre de la procédure d'importation pour les modules. Les classes que nous ajoutons sont des classes dérivées de PatchImporter , utilisant souvent l'interface plus facile fournie par PatchSimple , qui nous permettent d'envelopper des fonctions qui effectuent l'entrée / sortie dans une fonction qui appelle d'abord la destination pour enregistrer les informations.
Généralement, la majeure partie de la complexité est cachée dans PatchImporter et PatchSimple (plus utils.py ), donc le code réel pour envelopper un module, comme numpy est assez simple:
# Inherit from PatchSimple
class PatchNumpy ( PatchSimple ):
# Specify the full name of the module
modulename = 'numpy'
# List functions that are involved in input/output
# these can be anything that can go after "modulename."
# so they could be something like "pyplot.savefig" for example
input_functions = [ 'genfromtxt' , 'loadtxt' , 'load' , 'fromfile' ]
output_functions = [ 'save' , 'savez' , 'savez_compressed' , 'savetxt' ]
# Define the functions that will be used to wrap the input/output
# functions.
# In this case we are calling the log_input function to log it to the DB
# and we are giving it the 0th argument from the function (because all of
# the functions above take the filename as the 0th argument), and telling
# it that it came from numpy.
input_wrapper = create_wrapper ( log_input , 0 , 'numpy' )
output_wrapper = create_wrapper ( log_output , 0 , 'numpy' )Une classe comme celle-ci doit être implémentée pour chaque module dont l'entrée / sortie doit être journalisée. À l'heure actuelle, les fonctions d'entrée et de sortie suivantes sont corrigées:
Ce tableau répertorie les modules que la destination a des correctifs et les fonctions d'entrée et de sortie qui sont corrigées.
| Module | Fonctions d'entrée | Fonctions de sortie |
|---|---|---|
pandas | read_csv , read_table , read_excel , read_hdf , read_pickle , read_stata , read_msgpack | DataFrame.to_csv , DataFrame.to_excel , DataFrame.to_hdf , DataFrame.to_msgpack , DataFrame.to_stata , DataFrame.to_pickle , Panel.to_excel , Panel.to_hdf , Panel.to_msgpack , Panel.to_pickle , Series.to_csv , Series.to_hdf , Series.to_msgpack , Series.to_pickle |
matplotlib.pyplot | savefig | |
numpy | genfromtxt , loadtxt , fromfile | save , savez , savez_compressed , savetxt |
lxml.etree | parse , iterparse | |
bs4 | BeautifulSoup | |
gdal | Open | Driver.Create , Driver.CreateCopy |
sklearn | datasets.load_svmlight_file | datasets.dump_svmlight_file |
nibabel | nifti1.Nifti1Image.from_filename , nifti2.Nifti2Image.from_filename , freesurfer.mghformat.MGHImage.from_filename , spm99analyze.Spm99AnalyzeImage.from_filename , minc1.Minc1Image.from_filename , minc2.Minc2Image.from_filename , analyze.AnalyzeImage.from_filename , parrec.PARRECImage.from_filename , spm2analyze.Spm2AnalyzeImage.from_filename | nifti1.Nifti1Image.to_filename , nifti2.Nifti2Image.to_filename , freesurfer.mghformat.MGHImage.to_filename , spm99analyze.Spm99AnalyzeImage.to_filename , minc1.Minc1Image.to_filename , minc2.Minc2Image.to_filename analyze.AnalyzeImage.to_filename parrec.PARRECImage.to_filename spm2analyze.Spm2AnalyzeImage.to_filename |
Cependant, l'exemple de code ci-dessus montre à quel point il est facile d'écrire une classe pour envelopper un nouveau module - alors n'hésitez pas à soumettre une demande de traction pour faire fonctionner la destination avec vos modules scientifiques préférés!
Le cadre de test de la destination est dans integration_test . Le cadre de test a été conçu pour s'exécuter sous Python 2.7+ et Python 3+. Pour plus d'informations, voir le cadre de test de la destination.
Le cadre de test est exécuté sur les plates-formes suivantes: