Il y a des tas de choses qu’on peut vouloir faire au moment où un fichier change :
- Faire un backup;
- Lancer les tests unittaires;
- Démarrer un build;
- Recharger un contenu;
- Envoyer un email;
- Afficher une notification;
- Mettre à jour un listing.
Et ce n’est même pas dur à faire en Python grace à la lib watchdog :
pip install watchdog |
D’abord, on créer un handler, une classe qui va contenir le code à lancer quand il arrive quelque chose à nos fichiers :
from watchdog.events import FileSystemEventHandler class MonHandler(FileSystemEventHandler): # cette méthode sera appelée à chaque fois qu'un fichier # est modifié def on_modified(self, event): print("Ah, le fichier %s a été modifé" % event.src_path) # On peut aussi implémenter les méthodes suivantes : # - on_any_event(self, event) # - on_moved(self, event) # - on_created(self, event) # - on_deleted(self, event) # - on_modified(self, event) |
Ensuite on créé un observer, qui va lancer un thread dans lequel il va… observer :
from watchdog.observers import Observer observer = Observer() # Surveiller récursivement tous les événements du dossier /tmp # et appeler les méthodes de MonHandler quand quelque chose # se produit observer.schedule(MonHandler(), path='/tmp', recursive=True) |
On démarre tout ça :
import time observer.start() # L'observer travaille dans un thread séparé donc on fait une # boucle infinie pour maintenir le thread principal # actif dans cette démo mais dans un vrai programme, # vous mettez votre taff ici. try: while True: time.sleep(1) except KeyboardInterrupt: # Ctrl + C arrête tout observer.stop() # on attend que tous les threads se terminent proprement observer.join() |
Pour les ones shot, watchdog vient avec la commande watchmedo
qui permet de lancer un commande shell en cas d’événement :
# lancer 'echo lefichier' pour chaque fichier python ou texte modifié de mon dossier utilisateur watchmedo shell-command --patterns="*.py;*.txt" --recursive --command='echo "${watch_src_path}"' /home/sam |
Personnellement j’aime le lundi.
Très pratique, je l’utilise dans un cadre professionnel.
Attention cependant :
– étant multi-plateformes, son comportement est beaucoup influencé par le système d’exploitation (par exemple la copie d’un fichier sous Windows entraîne une création puis une modification)
– il ne garantie en rien la bonne complétion de vos fichiers, il lèvera un événement au premier octet écrit, c’est à vous de contrôler le fichier en amont avant de le traiter.
On l’a utilisé dans tous les sens, donc si besoin => indexerror
Est-ce que Watchdog est la meilleure solution pour lancer des tests unitaires dans un projet Django ?
J’utilisais watchdog pour faire des tests d’intégration lors de l’écriture de ma thèse en latex. A chaque sauvegarde, ça lancait make et récupérait le valeur de retour. Si xelatex ne compilait pas, il m’affichait une pop-up. Un sacré gain de temps, car on trouve plus rapidement une errreur de syntaxe si elle se trouve deux lignes au dessus.
Tu peux expliciter un peu ta méthode ? Je ne suis pas sûr de voir comment tu la mettais en pratique.
@khamaileon : je n’ai aucune idée de ce que signifie “meilleure solution”.
@Paradox : il n’y a rien à faire, la méthode est appelée automatiquement à chaque fois qu’un fichier est modifié. event.src_path contient le chemin du fichier modifié, tu fais ensuite ce que bon te semble avec.
Si “Linux only”, quel est l’intérêt par rapport à son cousin Pyinotify https://github.com/seb-m/pyinotify?
Je connais Watchdog comme Pyinotify mais j’ai plutôt utilisé Pyinotify et je vois pas trop la différence sauf l’aspect multi-plateforme.
@khamaileon
il n’existe pas de meilleure solution.
Je gere ce genre de tache avec incron: http://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/
Mon pote front-dev lui prefere gruntJs
Et certains utilisent des git hook pre-commits.
Merci encore pr le post, je ne connaissais pas watchdog.
@ThomasG77: c’est pareil. La doc de watchdog est un peu mieux, et comme tu le dis, watchdog est crossplateform. Sinon c’est kiffkiff.
Et un petit code en exemple pour avoir la notification des modifications de fichiers entre le moment où le programme s’est arrêté et où il a été redémarré :
https://gist.github.com/serge-kilimoff/8163233
Je l’ai écrit il y a un moment, je ne sais pas si ça fonctionne toujours, c’est plutôt considérer comme une base de code pour un truc plus poussé (de mémoire, je l’avais fais pour reconstruire automatiquement du css avec clevercss).
Sympas ! Mais faut pas avoir trop de fichiers j’imagine sinon ça doit ramouiller.
@Sam @LeMeteore
Je me suis mal exprimé. Par meilleure solution j’entendais facile à mettre en place, rapide d’exécution, solution éprouvée, documentée, …
@LeMeteore
On utiliser aussi Grunt et Gulp pour le front. Je cherche donc à trouver un équivalent Python.
Merci à @Sam pour le post
@khamaileon: question trop vague, on ne connait pas tes besoins, les points de comparaison avec les autres solutions, les contraintes…. Mais d’une manière générale, watchdog est juste là pour faire la moitié du boulot : lancer un truc au changement d’un fichier. Pour du build, il faut aussi la notion de tache (comme par exemple le fait pydoit).
@Sam: Ok c’est limpide pour moi maintenant. Au final je lance simplement mes tests à chaque modification de fichiers avec la commande watchmedo: watchmedo shell-command –patterns=”*.py” –recursive –command=’python manage.py test -v2′
Thx
@Sam
“Mais faut pas avoir trop de fichiers j’imagine sinon ça doit ramouiller.”
En fait c’est beaucoup plus lourd que ça.
J’ai utilisé “inotify” (c’était pas du Python à l’époque, mais j’attaquais en batch) et je l’ai recommandé à un collègue sysadmin. Enchanté, il l’a mis sur un serveur de gestion de mises à jour et un jour…le serveur s’est effondré car il y avait près de 2000 mises à jour simultanées.
Il aurait fallu que les routines déclenchées appellent la mise en tampon avec “at” ; bref, mettre en file d’attente les tâches déclenchées par des événements…
En outre, j’ai voulu m’en servir sur un serveur Samba, pour qu’il purge un répertoire sous certaines conditions, et me lance des “copy” et des “rsync” sous d’autres conditions. Las ! Inotify, ça ne MARCHE PAS sur un répertoire Windows…(normal, hein? Le système de fichiers Win ne fournissant pas les mêmes événements fichiers que sous Linux)
Donc, le concept de portabilité sous Python, c’est pas un gadget, c’est une vraie bouée de sauvetage face aux lacunes des OS les uns par rapport aux autres. Autre exemple pour le troll : “virtualenv” et “fakeroot”…
Vala, vala,
Long life to Python !